using Fusion.Utilities.Generics;
using System;

namespace Fusion.Frameworks.Notification
{
    public class NotificationManager
    {
        private struct NotificationData
        {
            bool persistent;
            Action<INotificationData> action;

            public NotificationData(bool persistent, Action<INotificationData> action)
            {
                this.persistent = persistent;
                this.action = action;
            }

            public bool Persistent { get => persistent; }
            public Action<INotificationData> Action { get => action; }
        }

        private static OrderDictionary<object, OrderDictionary<int, NotificationData>> notificationMaps = new OrderDictionary<object, OrderDictionary<int, NotificationData>>();

        public static void Register(object o, int key, Action<INotificationData> action, bool persistent = false)
        {
            if (!notificationMaps.ContainsKey(o))
            {
                notificationMaps[o] = new OrderDictionary<int, NotificationData>();
            }

            notificationMaps[o][key] = new NotificationData(persistent, action);
        }

        public static void Remove(object o, int key)
        {
            OrderDictionary<int, NotificationData> notificationMap;
            if (notificationMaps.TryGetValue(o, out notificationMap))
            {
                notificationMap.Remove(key);
                if (notificationMap.Count == 0)
                {
                    Remove(o);
                }
            }
        }

        public static void Remove(object o)
        {
            notificationMaps.Remove(o);
        }

        public static void Send(int key, INotificationData data = null)
        {
            int objectCount = notificationMaps.Count;

            for (int objectIndex = 0; objectIndex < objectCount; objectIndex++)
            {
                OrderDictionary<int, NotificationData> notificationMap = notificationMaps.Get(objectIndex);
                int actionCount = notificationMap.Count;
                for (int actionIndex = 0; actionIndex < actionCount; actionIndex++)
                {
                    if (key == notificationMap.GetKey(actionIndex))
                    {
                        notificationMap.Get(actionIndex).Action?.Invoke(data);
                    }
                }
            }
        }

        public static void Clear()
        {
            int count = notificationMaps.Count;
            for (int index = count - 1; index >= 0; index--)
            {
                OrderDictionary<int, NotificationData> callbacks = notificationMaps.Get(index);
                int dataCount = callbacks.Count;
                for (int dataIndex = dataCount - 1; dataIndex >= 0; dataIndex--)
                {
                    if (!callbacks[dataIndex].Persistent)
                    {
                        callbacks.RemoveAt(dataIndex);
                    }
                }
                if (callbacks.Count == 0)
                {
                    notificationMaps.RemoveAt(index);
                }
            }
        }
    }
}


